﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mimoza.Transport;
using Mimoza.Common;

namespace Mimoza.DBLayer.Transport
{
    public interface Repository<TRecord>
    {
        void Insert(TRecord record);
        void Update(TRecord record);
        void Delete(TRecord record);
    }

    public class ServerRepository<TRecord, TRepositoryCustomRequest, TBaseRepository>:
        Repository<TRecord>
        where TBaseRepository : class, Mimoza.Common.IDBObjectChangedNotfier
        where TRepositoryCustomRequest : class, new()
        where TRecord : class, new()
    {
        public ServerRepository(TBaseRepository baseRepository,
            Server.CollectionManager collectionManager,
            Server.ConnectionList connectionList)
        {
            if (baseRepository == null)
            {
                throw new ArgumentException();
            }

            m_dbBaseRepository = baseRepository;
            m_collectionManager = collectionManager;
            m_connectionList = connectionList;

            RepositoryReply<TRecord>.RegisterInTransport();
            RepositoryEventReply.RegisterInTransport();

            m_dbBaseRepository.OnObjectChanged += DBObjectChangedEventHandler;

            if (OnObjectChanged!=null) OnObjectChanged = null;
        }

        public TBaseRepository BaseRepository
        {
            get { return m_dbBaseRepository; }
        }

        public Server.CollectionManager CollectionManager
        {
            get { return m_collectionManager; }
        }

        public RepositoryReply<TRecord> RegisterCollection<TCollectionRecord>(IEnumerable<TCollectionRecord> enumerator,
            Connection conn) where TCollectionRecord : class, new()
        {
            Collection<TCollectionRecord> collection =
                new Collection<TCollectionRecord>(enumerator);

            CollectionManager.AddCollection(collection, conn);

            return new RepositoryReply<TRecord>(collection.CollectionID);
        }

        virtual public TransferObject ProcessRequest(Connection conn, TRepositoryCustomRequest request)
        {
            return null;
        }

        public void Subscribe(Connection conn)
        {
            // Not supported: comment see in EventManager.EventPublishNotify
            // m_eventManager.Subscribe(conn, BaseRepository);
        }

        void DBObjectChangedEventHandler(object sender, DBObjectChangedEventArgs e)
        {
            RepositoryEventRequest<TRecord> eventRequest = new RepositoryEventRequest<TRecord>(
                e.Object as TRecord, e.EventType);

            m_connectionList.SendRequest(eventRequest);
        }

        virtual public void Insert(TRecord record) { }
        virtual public void Update(TRecord record) { }
        virtual public void Delete(TRecord record) { }

        public event EventHandler<Mimoza.Common.DBObjectChangedEventArgs> OnObjectChanged;

        TBaseRepository m_dbBaseRepository;
        Server.CollectionManager m_collectionManager;
        Server.ConnectionList m_connectionList;
        
        // Not supported: comment see in EventManager.EventPublishNotify
        // Server.EventManager<TRecord> m_eventManager = new Mimoza.DBLayer.Transport.Server.EventManager<TRecord>();
    }

    public class ClientRepository<TRecord> :
        Repository<TRecord>
        where TRecord : class, new()
    {
        static ClientRepository()
        {
            CollectionReply<TRecord>.RegisterInTransport();
            RepositoryEventReply.RegisterInTransport();
        }

        public event EventHandler<Mimoza.Common.DBObjectChangedEventArgs> OnObjectChanged = null;

        public ClientRepository()
        {
            RepositoryEventRequest<TRecord>.RegisterHandler(
                new TransferObjectGeneric<RepositoryEventRequest<TRecord>, RepositoryEventReply>.Handler(
                ProcessRepositoryEventRequest));
        }

        virtual public Connection Connection
        {
            get { return m_connection; }
            set 
            { 
                m_connection = value;
                MakeSubscription();
            }
        }

        public void SendRequest(TransferObject request)
        {
            TransferObject reply;
            Connection.MakeRequest(request, out reply);
        }

        public TRecord MakeObjectRequest(TransferObject request)
        {
            RepositoryReply<TRecord> reply = MakeRequest(request);

            return reply.Object;
        }

        public ClientCollectionEnumerable<TCollectionRecord> MakeCollectionRequest<TCollectionRecord>(TransferObject request)
            where TCollectionRecord : class, new()
        {
            RepositoryReply<TRecord> reply = MakeRequest(request);

            if (reply.Collection == null || reply.Collection.ID == null)
            {
                throw new ArgumentException();
            }

            return new ClientCollectionEnumerable<TCollectionRecord>(
                Connection, reply.Collection.ID);
        }

        public RepositoryReply<TRecord> MakeRequest(TransferObject request)
        {
            lock (Connection)
            {
                try
                {
                    TransferObject reply;
                    Connection.MakeRequest(request, out reply);

                    if (reply == null)
                    {
                        throw new ArgumentException();
                    }

                    RepositoryReply<TRecord> repositoryReply = reply as RepositoryReply<TRecord>;
                    if (repositoryReply == null)
                    {
                        throw new ArgumentException();
                    }

                    return repositoryReply;

                }
                catch (Exception e)
                {
                    Logger.Log.Error("TransportDBLayer: TransferRepository.MakeRequest error - '" + e.ToString() + "'.");
                    throw e;
                }
            }
        }

        RepositoryEventReply ProcessRepositoryEventRequest(RepositoryEventRequest<TRecord> request)
        {
            if (OnObjectChanged != null)
            {
                OnObjectChanged(this, new Mimoza.Common.DBObjectChangedEventArgs(request.Object, request.EventType));
            }

            return new RepositoryEventReply();
        }

       
        virtual public void Insert(TRecord record) 
        {
        }

        virtual public void Update(TRecord record) 
        {
        }

        virtual public void Delete(TRecord record) 
        {
        }

        private void MakeSubscription()
        {
            // Not supported: comment see in EventManager.EventPublishNotify
            // SendRequest(new DBContainersTranferRequest(Operation.Subscribe, null));
        }

        Connection m_connection = null;
    }

    public class RecordTransfer<TRecord>
    {
        public TRecord Object
        {
            get { return m_oject; }
            set { m_oject = value; }
        }

        TRecord m_oject;
    }

    public enum Operation
    {
        None,
        Insert,
        Update,
        Delete,
        Subscribe,
        Custom
    };

    public class RepositoryRequest<TRecord, TRepository, TRepositoryCustomRequest, TBaseRepository> 
        where TRecord : class, new()
        where TRepositoryCustomRequest : class, new()
        where TRepository : ServerRepository<TRecord, TRepositoryCustomRequest, TBaseRepository>
        where TBaseRepository : class, Mimoza.Common.IDBObjectChangedNotfier
    {
        public Operation Operation
        {
            get
            {
                return m_operation;
            }
            set
            {
                m_operation = value;
            }
        }

        public TRecord Object
        {
            get
            {
                return (m_object != null) ? m_object.Object : null;
            }
            set
            {
                if (m_object == null) m_object = new RecordTransfer<TRecord>(); 
                m_object.Object = value;
            }
        }

        public TRepositoryCustomRequest CustomRequest
        {
            get
            {
                return m_customRequest;
            }
            set
            {
                m_customRequest = value;
            }
        }

        public TransferObject Process(Connection conn, TRepository repository)
        {
            RepositoryReply<TRecord> defaultReply = new RepositoryReply<TRecord>(Object);
            
            TransferObject returnObject = defaultReply; 
            if (repository == null)
            {
                return null;
            }

            switch (m_operation)
            {
                case Operation.Insert:
                    repository.Insert(Object);
                    break;
                case Operation.Update:
                    repository.Update(Object);
                    break;
                case Operation.Delete:
                    repository.Delete(Object);
                    break;
                case Operation.Subscribe:
                    repository.Subscribe(conn);
                    break;
                case Operation.Custom:
                    returnObject = repository.ProcessRequest(conn, m_customRequest);
                    break;
                default:
                    break;
            }

            return returnObject;
        }

        Operation m_operation = Operation.None;
        RecordTransfer<TRecord> m_object = null;
        TRepositoryCustomRequest m_customRequest = null;
    }

    public class RepositoryReply<TRecord> :
        TransferObjectGenericReply<RepositoryReply<TRecord>>
        where TRecord : class, new()
    {
        public RepositoryReply()
        {
        }

        public RepositoryReply(TRecord obj)
        {
            Object = obj;
        }

        public RepositoryReply(Guid collectionId)
        {
            Collection = new CollectionTransfer(collectionId);
        }

        public TRecord Object
        {
            get 
            {
                return (m_object != null) ? m_object.Object : null; 
            }
            set
            {
                if (m_object == null)
                {
                    m_object = new RecordTransfer<TRecord>();
                }
                m_object.Object = value;
            }
        }

        public CollectionTransfer Collection
        {
            get
            {
                return m_collection;
            }
            set
            {
                m_collection = value;
            }
        }

        RecordTransfer<TRecord> m_object = null;
        CollectionTransfer m_collection = null;
    }

    public class RepositoryEventRequest<TRecord> :
        TransferObjectGeneric<RepositoryEventRequest<TRecord>,RepositoryEventReply>
        where TRecord : class, new()
    {
        public RepositoryEventRequest()
        {
        }

        public RepositoryEventRequest(TRecord _obj,
            Mimoza.Common.DBEventType _type)
        {
            m_obj = _obj;
            m_type = _type;
        }

        public TRecord Object
        {
            get { return m_obj; }
            set { m_obj = value; }
        }

        public Mimoza.Common.DBEventType EventType
        {
            get { return m_type; }
            set { m_type = value; }
        }

        Mimoza.Common.DBEventType m_type;
        TRecord m_obj;
    }

    public class RepositoryEventReply :
        TransferObjectGenericReply<RepositoryEventReply>
    {
        public RepositoryEventReply()
        {
        }
    }

}
